package dal

import (
	"encoding/json"
	"entrytask/pkg/error"
	"entrytask/rpc/config"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"
)

type User struct {
	Id       int64  `json:"id"`
	UserName string `json:"username"`
	Password string `json:"password"`
	Nickname string `json:"nickname"`
	Profile  string `json:"profile"`
}

func (user *User) SelectUserByName(name string) error {
	stmt, err := config.DB.Prepare("SELECT username,password,nickname,profile FROM t_user WHERE username=?")
	if err != nil {
		return err
	}
	defer stmt.Close()

	rows, err := stmt.Query(name)
	defer rows.Close()
	if err != nil {
		// fixme: query user fail, name:  iron_man_161 Error 1040: Too many connections
		// query user fail, name:  iron_man_4371824 commands out of sync. You can't run this command now
		fmt.Println("query user fail, name: ", name, err)
		return err
	}
	// 数据处理
	for rows.Next() {
		rows.Scan(&user.UserName, &user.Password, &user.Nickname, &user.Profile)
	}
	if err := rows.Err(); err != nil {
		return err
	}
	return nil
}

// SelectPassword mysql密码进行加密
func SelectPassword(password string) string {
	// todo 性能消耗大户 wait to fix
	start := time.Now().UnixNano()
	defer func() {
		end := time.Now().UnixNano()
		log.Printf("user dal using db encrypt password[%s] consume time: %d ns", password, end-start)
	}()

	rows, err := config.DB.Query("SELECT UPPER(SHA1(UNHEX(SHA1(?))))", password)
	if err != nil {
		log.Printf("select password err: %v", err)
		return ""
	}
	defer rows.Close()

	if rows.Next() {
		var encoded string
		err := rows.Scan(&encoded)
		if err == nil {
			return encoded
		}
	}
	log.Printf("scan result err: %v ", err)
	return ""
}

func (user *User) SelectUserWithCheckPassWordByName(username string, requestPassword string) (bool, error) {
	// todo 性能消耗大户 wait to fix
	start := time.Now().UnixNano()
	defer func() {
		end := time.Now().UnixNano()
		log.Printf("SelectUserWithCheckPassWordByName using db encrypt password[%s] consume time: %d ns", requestPassword, end-start)
	}()

	rows, err := config.DB.Query("SELECT username,password,nickname,profile, UPPER(SHA1(UNHEX(SHA1(?)))) as requestPassword FROM t_user WHERE username=?", requestPassword, username)
	if err != nil {
		log.Printf("select user with password err: %v", err)
		return false, err
	}
	defer rows.Close()
	if err != nil {
		fmt.Println("query user fail, name: ", username, err)
		return false, err
	}
	// 数据处理
	for rows.Next() {
		rows.Scan(&user.UserName, &user.Password, &user.Nickname, &user.Profile, &requestPassword)
	}
	if err := rows.Err(); err != nil {
		return false, err
	}
	return strings.Compare(user.Password, requestPassword) == 0, nil
}

func (user *User) UpdateUserByName(username string, nickname string, picPath string) int64 {
	if username == "" {
		return 0
	}
	// picPath为空则无需更新 todo 测试sql注入场景/err返回
	if picPath == "" {
		nums := Update("UPDATE t_user set nickname=? where username=?", nickname, username)
		return nums
	} else {
		nums := Update("UPDATE t_user set nickname=?, profile=? where username=?", nickname, picPath, username)
		return nums
	}
}

// Validate the fields.
func (user *User) Validate() error {
	if user.UserName == "" || user.Password == "" {
		return errors.New(errno.ErrValidation.Message)
	}
	return nil
}

func (user *User) Create() (int64, error) {
	id, err := Insert("INSERT INTO  t_user(username,password,nickname,profile) values (?,?,?,?)", user.UserName, user.Password, user.Nickname, user.Profile)
	if err != nil {
		return 1, err
	}

	return id, nil
}

// SelectUsernameList 查询出size个用户名
func (user *User) SelectUsernameList(size int) ([]string, error) {
	// SELECT username FROM t_user ORDER BY rand() LIMIT ?
	stmt, err := config.DB.Prepare("SELECT username FROM t_user LIMIT ?")
	if err != nil {
		return make([]string, 0, 0), err
	}
	defer stmt.Close()

	rows, err := stmt.Query(size)
	if err != nil {
		return make([]string, 0, 0), err
	}
	defer rows.Close()

	var results []string
	for rows.Next() {
		var item string
		err := rows.Scan(&item)
		if err != nil {
			log.Printf("scan username row err:%v", err)
			return results, err
		}
		results = append(results, item)
	}
	return results, nil
}

func (user *User) UserToJson() string {
	jsonStr, err := json.Marshal(user)
	if err != nil {
		log.Println(err)
	}
	return string(jsonStr)
}

func (user *User) JsonToUser(jsonBlob string) error {
	err := json.Unmarshal([]byte(jsonBlob), &user)
	if err != nil {
		return err
	}
	return nil
}
